#include <iostream>
#include "../include/Masque.h"
#include "../include/Image.h"
#include "../include/Pixel.h"

Masque::Masque(int _l,int _h) : largeur(_l),hauteur(_h)
{
	data = new GLubyte[largeur*hauteur];
	dataTmp = new GLubyte[largeur*hauteur];
	for(int i=0;i<largeur*hauteur;i++){
		data[i] = 0;
	}
	taillePinceau = 5;
}

Masque::~Masque(){
	delete []data;
}

GLubyte Masque::Lit(int x,int y,bool tmp){
	if (x < 0)
		x = -x;
	else if (x >= largeur)
		x = largeur*2 - 1 - x;
	if (y < 0)
		y = -y;
	else if (y >= hauteur)
		y = hauteur*2 -1 - y;
	if(!tmp)
		return data[y*largeur + x];
	else
		return dataTmp[y*largeur + x];
}

void Masque::getBounds ( int * _xmin, int * _xmax, int * _ymin, int * _ymax )
{
	int xmin = largeur;
	int xmax = 0;
	int ymin = hauteur;
	int ymax = 0;
	for ( int y = 0; y < hauteur; ++y )
	{
		for ( int x = 0; x < largeur; ++x )
		{
			if ( data[y*largeur + x] != 0 )
			{
				if ( x < xmin )
					xmin = x;
				if ( x > xmax )
					xmax = x;
				if ( y < ymin )
					ymin = y;
				if ( y > ymax )
					ymax = y;
			}
		}
	}
	*_xmin = xmin;
	*_xmax = xmax;
	/**_ymin = ymin;
	*_ymax = ymax;*/
	*_ymin = hauteur - 1 - ymax;
	*_ymax = hauteur - 1 - ymin;
}

bool Masque::LitBool(int x,int y,bool tmp){
	GLubyte val = Lit(x,y,tmp);

	if(val==0)
		return false;
	else
		return true;
}

void Masque::Ecrit(int _x,int _y,GLubyte _b,bool tmp){
	if(_x>=0&&_x<largeur&&_y>=0&&_y<hauteur){
		if(!tmp)
			data[_y*largeur + _x] = _b;
		else
			dataTmp[_y*largeur + _x] = _b;
	}
}

void Masque::Dessine(int xa,int ya,int xb, int yb,int couleur){

	int x,y,dx,dy,incrmX,incrmY,dp,NE,SE;

	dx = xb-xa;
	dy = yb-ya;

	if (dx>0)
		incrmX = 1;
	else {
		incrmX = -1;
		dx *= -1;
	}

	if (dy>0)
		incrmY = 1;
	else {
		incrmY = -1;
		dy *= -1;
	}


	if (dx>=dy) {
		dp=2*dy-dx;
		SE=2*dy;
		NE=2*(dy-dx);

		y=ya;
		for(x=xa;x!=xb;x+=incrmX) {
			for(int i=x-taillePinceau;i<=x+taillePinceau;i++){
				for(int j=y-taillePinceau;j<=y+taillePinceau;j++){
					Ecrit(i,j,couleur);
				}
			}

			if (dp<=0)
				dp += SE;
			else {
				dp += NE;
				y+=incrmY;
			}
		}
	}
	else if (dx<dy) {
		dp=2*dx-dy;
		SE=2*dx;
		NE=2*(dx-dy);

		x=xa;
		for(y=ya;y!=yb;y+=incrmY) {
			for(int i=x-taillePinceau;i<=x+taillePinceau;i++){
				for(int j=y-taillePinceau;j<=y+taillePinceau;j++){
					Ecrit(i,j,couleur);
				}
			}

			if (dp<=0)
				dp += SE;
			else {
				dp += NE;
				x+=incrmX;
			}
		}
	}
	if(xa==xb && ya==yb)
		for(int i=x-taillePinceau;i<=x+taillePinceau;i++){
			for(int j=y-taillePinceau;j<=y+taillePinceau;j++){
				Ecrit(i,j,couleur);
			}
		}
}

void Masque::Effacer(){
	for(int i=0;i<largeur*hauteur;i++){
		data[i] = 0;
	}
}

void Masque::Afficher(){
	glDrawPixels(largeur, hauteur, GL_ALPHA, GL_UNSIGNED_BYTE, data) ;
}

void Masque::AffichePinceau(int x,int y){

	glLineWidth(1);
	glColor3f(1,1,1);

	glBegin(GL_LINE_LOOP);
		glVertex2i(x-taillePinceau,y-taillePinceau);
		glVertex2i(x+taillePinceau,y-taillePinceau);
		glVertex2i(x+taillePinceau,y+taillePinceau);
		glVertex2i(x-taillePinceau,y+taillePinceau);
	glEnd();
}

void Masque::setTaillePinceau(int t){
	if(t>=0 && t<300)
		taillePinceau = t;
}
int Masque::getTaillePinceau(){
	return taillePinceau;
}

void Masque::getMaskFromPicture ( Image* I, Pixel p )
{
	for ( int i = 0 ; i < I->getL() ; i++ )
		for ( int j = 0 ; j < I->getH() ; j++ )
		{
			if ( I->LitPixel(i,j).getR() == p.getR() && I->LitPixel(i,j).getB() == p.getB() && I->LitPixel(i,j).getV() == p.getV() )
			{
				Ecrit(i,hauteur-j,255);
				//this->Ecrit(i,this->hauteur-j,255);
				//this->Ecrit(i,this->hauteur-j-1,255);
				//this->Ecrit(i,this->hauteur-j+1,255);
				//this->Ecrit(i-1,this->hauteur-j-1,255);
				//this->Ecrit(i-1,this->hauteur-j,255);
				//this->Ecrit(i-1,this->hauteur-j+1,255);
				//this->Ecrit(i+1,this->hauteur-j-1,255);
				//this->Ecrit(i+1,this->hauteur-j,255);
				//this->Ecrit(i+1,this->hauteur-j+1,255);
			}
		}
}

void Masque::empiler(){
	listePos.clear();
	for(int x=0;x<largeur;x++){
		for(int y=0;y<hauteur;y++){
			if(LitBool(x,y))
				listePos.push_back(Point(x,y,Lit(x,y)));
		}
	}
}




GLubyte Masque::operator()(const int x, const int y,bool tmp){
	return Lit(x,y,tmp);
}

void Masque::operator()(const int x, const int y,GLubyte val,bool tmp){
	Ecrit(x,y,val,tmp);
}

// Fonction dilatation
void Masque::dilatation (int ordre){

	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x < largeur;x++){
			Ecrit(x,y,0,true);
		}
	}

	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x < largeur;x++){
			if(LitBool(x,y,false)){
				for(int dy=y-ordre;dy<=y+ordre;dy++){
					for(int dx=x-ordre;dx<=x+ordre;dx++){
						Ecrit(dx,dy,255,true);
					}
				}
			}
		}
	}
}

// Fonction erosion
void Masque::erosion (int ordre){
	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x< largeur;x++){
			Ecrit(x,y,255-Lit(x,y),false);
		}
	}
	dilatation(ordre);
	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x< largeur;x++){
			Ecrit(x,y,255-Lit(x,y),false);
			Ecrit(x,y,255-Lit(x,y,true),true);
		}
	}
}
bool Masque::vide(){
	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x< largeur;x++){
			if(LitBool(x,y))
				return false;
		}
	}
	return true;
}

void Masque::Traitement(){
	GLubyte * tmp,*swap;
	int cpt = 0;
	tmp = new GLubyte[hauteur*largeur];

	while(!vide()){
		cpt++;
		for(int y = 0; y < hauteur; y++){
			for(int x = 0; x< largeur;x++){
				if(LitBool(x,y))
					tmp[y*largeur+x] = cpt;
			}
		}
		erosion(1);
		swap = dataTmp;
		dataTmp = data;
		data = swap;
	}
	cpt++;
	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x< largeur;x++){
			if(tmp[y*largeur+x]!=0)
				tmp[y*largeur+x] = cpt-tmp[y*largeur+x];
		}
	}
	swap = data;
	data = tmp;
	delete swap;

}

bool Masque::Update(){
	bool r=false;
	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x< largeur;x++){
			if(Lit(x,y)>1){
				Ecrit(x,y,Lit(x,y)-1);
				r=true;
			}
		}
	}
	return r;
}

void Masque::getBande(){
	erosion(2);
	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x< largeur;x++){
			if(LitBool(x,y,true)){
				Ecrit(x,y,0);
			}
		}
	}
}

bool Masque::getNextBande(){
	GLubyte* swap;
	bool erodeUltime = true;

	swap = dataTmp;
	dataTmp = data;
	data = swap;

	dilatation(2);

	swap = dataTmp;
	dataTmp = data;
	data = swap;

	erosion(3);

	for(int y = 0; y < hauteur; y++){
		for(int x = 0; x< largeur;x++){
			if(Lit(x,y,true)){
				erodeUltime = false;
				Ecrit(x,y,0);
			}
		}
	}
	if(erodeUltime){
		swap = dataTmp;
		dataTmp = data;
		data = swap;
		return false;
	}
	return true;
}
